import { uniqueId } from './index'
import { PostMessageType, type PostMessageEvent, Certificate } from './types'

chrome.runtime?.onMessage.addListener((event: PostMessageEvent, sender) => {
  if (event?.uniqueId !== uniqueId) return void 0

  if (event.type === PostMessageType.HEARTBEAT) {
    const tabId = sender.tab?.id
    tabId && heartbeat.get(tabId)?.forEach((fn) => fn(event.data))
    return
  }

  emitMessage(event.type, event.data)
})

const pool = new Map<any, Set<(data: any) => void>>()

function emitMessage(type: PostMessageType, data: any) {
  pool.get(type)?.forEach((handler) => handler(data))
}

/**
 * 监听其它端发送的信息
 * @param type 事件类型
 * @param handler 事件回调
 */
export function onTabMessage(type: PostMessageEvent['type'], handler: (data: any) => void) {
  let set = pool.get(type)
  if (!set) pool.set(type, (set = new Set()))
  set.add(handler)
}

/**
 * 取消对其它端信息的监听
 * @param type 事件类型
 * @param handler 事件回调
 */
export function offTabMessage(type: PostMessageType, handler: any) {
  pool.get(type)?.delete(handler)
}

export function sendTabMessage(tabId: number | undefined, data: PostMessageEvent) {
  tabId && chrome.tabs.sendMessage(tabId, { uniqueId, ...data })
}

export function sendContextMessage(data: PostMessageEvent) {
  chrome?.runtime.sendMessage({ uniqueId, ...data })
}

const heartbeat = new Map<number, Set<(...args: any) => void>>()

function clearHeartbeat(tabId: number, fn: (...args: any) => void) {
  const queue = heartbeat.get(tabId)
  if (queue) {
    queue.delete(fn)
    queue.size && heartbeat.delete(tabId)
  }
}

export function heartbeatDetection(tabId: number, certificate: Certificate) {
  return new Promise<void>((resolve, reject) => {
    let timer: number | null = setTimeout(() => {
      if (timer) {
        reject()
        clearHeartbeat(tabId, success)
        timer = null
      }
    }, 3000)

    function success(value: any) {
      clearHeartbeat(tabId, success)
      if (timer && value === certificate) {
        resolve()
        clearTimeout(timer)
        timer = null
      }
    }

    let queue = heartbeat.get(tabId)
    if (!queue) {
      heartbeat.set(tabId, (queue = new Set<() => void>()))
    }
    queue.add(success)

    sendTabMessage(tabId, { type: PostMessageType.HEARTBEAT, data: certificate })
  })
}
